#!/usr/bin/perl -w
#
# $Id: PacketWindow.pm,v 1.22 2003/08/04 23:13:18 solovam Exp $
#
# This file is a part of gkismet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

#
# PacketWindow class
#
package PacketWindow;

use Gtk;
use Misc;
use ClistWindow;
use strict;

@PacketWindow::ISA = qw(ClistWindow);

my %protocol_info_type = (proto_unknown => 0, proto_udp => 1, proto_misc_tcp => 2, proto_arp => 3, proto_dhcp_server => 4, proto_cdp => 5,
	proto_netbios => 6, proto_netbios_tcp => 7, proto_ipx => 8, proto_ipx_tcp => 9, proto_turbocell => 10,
	proto_netstumbler => 11, proto_lucenttest => 12, proto_wellenreiter => 13, proto_gstsearch => 14);
my %protocol_netbios_type = (proto_netbios_unknown => 0, proto_netbios_host => 1, proto_netbios_master => 2, proto_netbios_domain => 3,
	proto_netbios_query => 4, proto_netbios_pdcquery => 5); 
my %packet_type = (packet_noise => -2,	# We're too short or otherwise corrupted
	packet_unknown => -1,		# What are we?
	packet_management => 0,		# LLC management
	packet_phy => 1,		# Physical layer packets, most drivers can't provide these
	packet_data => 2);		# Data frames
my %packet_sub_type = (
	packet_sub_unknown => -1,
	# Management subtypes
	packet_sub_association_req => 0,
	packet_sub_association_resp => 1,
	packet_sub_reassociation_req => 2,
	packet_sub_reassociation_resp => 3,
	packet_sub_probe_req => 4,
	packet_sub_probe_resp => 5,
	packet_sub_beacon => 8,
	packet_sub_atim => 9,
	packet_sub_disassociation => 10,
	packet_sub_authentication => 11,
	packet_sub_deauthentication => 12,
	# Phy subtypes
	packet_sub_rts => 11,
	packet_sub_cts => 12,
	packet_sub_ack => 13,
	packet_sub_cf_end => 14,
	packet_sub_cf_end_ack => 15,
	# Data subtypes
	packet_sub_data => 0,
	packet_sub_data_cf_ack => 1,
	packet_sub_data_cf_poll => 2,
	packet_sub_data_cf_ack_poll => 3,
	packet_sub_data_null => 4,
	packet_sub_cf_ack => 5,
	packet_sub_cf_ack_poll => 6);
my %distribution_type = (no_distribution => 0, from_distribution => 1, to_distribution => 2, inter_distribution => 3, adhoc_distribution => 4);
my %turbocell_type = (
	turbocell_unknown => 0,
	turbocell_ispbase => 1, 	# 0xA0
	turbocell_pollbase => 2,	# 0x80
	turbocell_nonpollbase => 3,	# 0x00
	turbocell_base => 4);		# 0x40

#
# Tell connection to start sending the info
#
sub activateConnection
{
	my $self = shift;
	if($self->{'gKismetApplication'}->countObservers('PacketWindow') < 1)
	{
		$self->{'connection'}->enablePackets();
	}
}

#
# Tell connection to stop sending the info
#
sub deactivateConnection
{
	my $self = shift;
	if($self->{'gKismetApplication'}->countObservers('PacketWindow') == 1)
	{
		$self->{'connection'}->disablePackets();
	}
}

#
# Is it a worthwhile update?
#
sub isInterstingUpdate
{
	my $self = shift;
	my $data = shift;

	if($data->{'changed'} eq 'packet' && $self->{'connection'}->getPacket()->{'bssid'} eq $self->{'bssid'})
	{
		return $true;
	}
	else
	{
		return $false;
	}
}

#
# Window name (title)
#
sub getWindowName
{
	return 'Packet dump';
}

#
# Titles for CList columns
#
sub getColumnTitles
{
        return ('Time', 'BSSID', 'SSID', 'Source MAC', 'Destination MAC', 'Packet Type', 'Packet Info');
}

#
# How many lines to show in window
#
sub getWindowDepth
{
	my $self = shift;
	return $self->{'gKismetApplication'}{'preferences'}->getPref('packetDepth');
}


#
# Get column data
#
sub getColumnData
{
	my $self = shift;

	my $packet = $self->{'connection'}->getPacket();
	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($packet->{'timesec'});
	my $time = sprintf("%.2d:%.2d:%.2d", $hour, $min, $sec);
	my $bssid = $packet->{'bssid'};
	my $ssid = $packet->{'ssid'};
	my $srcmac = $packet->{'sourcemac'};
	my $dstmac = $packet->{'destmac'};
	my $ptype = '';
	my $pinfo = '';
	if($packet->{'type'} == $packet_type{'packet_noise'})
	{
		$ptype = 'NOISE';
	}
	elsif($packet->{'type'} == $packet_type{'packet_unknown'})
	{
		$ptype = 'UNKNOWN';
	}
	elsif($packet->{'type'} == $packet_type{'packet_management'} )
	{
		$ptype = 'MANAGEMENT';
		if($packet->{'subtype'} == $packet_sub_type{'packet_sub_association_req'})
		{
			$pinfo = "Association Request";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_association_resp'})
		{
			$pinfo = "Association Response";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_reassociation_req'})
		{
			$pinfo = "Reassociation Request";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_reassociation_resp'})
		{
			$pinfo = "Reassociation Response";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_probe_req'})
		{
			$pinfo = "Probe Request";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_probe_resp'})
		{
			$pinfo = "Probe Response";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_beacon'})
		{
			$pinfo = "Beacon";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_atim'})
		{
			$pinfo = "ATIM";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_disassociation'})
		{
			$pinfo = "Disassociation";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_authentication'})
		{
			$pinfo = "Authentication";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_deauthentication'})
		{
			$pinfo = "Deauthentication";
		}
		else
		{
			$pinfo = "Unknown";
		}
	}
	elsif($packet->{'type'} == $packet_type{'packet_phy'})
	{
		$ptype = 'PHY';
		if($packet->{'subtype'} == $packet_sub_type{'packet_sub_rts'})
		{
			$pinfo = "Ready To Send";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_cts'})
		{
			$pinfo = "Clear To Send";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_ack'})
		{
			$pinfo = "Data Ack";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_cf_end'})
		{
			$pinfo = "CF End";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_cf_end_ack'})
		{
			$pinfo = "CF End+Ack";
		}
		else
		{
			$pinfo = "Unknown";
		}
	}
	elsif($packet->{'type'} == $packet_type{'packet_data'})
	{
		$ptype .= 'DATA';
		if($packet->{'encrypted'})
		{
			$pinfo = 'Encrypted ';
		}
		if($packet->{'weak'})
		{
			$pinfo = 'Weak ';
		}
		if($packet->{'subtype'} == $packet_sub_type{'packet_sub_data'})
		{
			if($packet->{'prototype'} == $protocol_info_type{'proto_netbios'} ||
				$packet->{'prototype'} == $protocol_info_type{'proto_netbios_tcp'})
			{
				$pinfo .= "NETBIOS";
				if($packet->{'nbtype'} == $protocol_netbios_type{'proto_netbios_host'})
				{
					$pinfo .= "HOST ";
				}
				elsif($packet->{'nbtype'} == $protocol_netbios_type{'proto_netbios_master'})
				{
					$pinfo .= "MASTER ";
				}
				elsif($packet->{'nbtype'} == $protocol_netbios_type{'proto_netbios_domain'})
				{
					$pinfo .= "DOMAIN ";
				}
				elsif($packet->{'nbtype'} == $protocol_netbios_type{'proto_netbios_query'})
				{
					$pinfo .= "QUERY ";
				}
				elsif($packet->{'nbtype'} == $protocol_netbios_type{'proto_netbios_pdcquery'})
				{
					$pinfo .= "PDC QUERY ";
				}
				$pinfo .= $packet->{'nbsource'};
			}
			elsif($packet->{'prototype'} == $protocol_info_type{'proto_udp'} ||
				$packet->{'prototype'} == $protocol_info_type{'proto_dhcp_server'})
			{
				$pinfo .= 'UDP ' . $packet->{'sourceip'} . ':' . PacketWindow->xgetservbyport($packet->{'sourceport'}, 'udp') .
					" -> " . $packet->{'destip'} . ':' . PacketWindow->xgetservbyport($packet->{'destport'}, 'udp');
			}
			elsif($packet->{'prototype'} == $protocol_info_type{'proto_misc_tcp'})
			{
				$pinfo .= 'TCP ' . $packet->{'sourceip'} . ':' . PacketWindow->xgetservbyport($packet->{'sourceport'}, 'tcp') .
					" -> " . $packet->{'destip'} . ':' . PacketWindow->xgetservbyport($packet->{'destport'}, 'tcp');
			}
			elsif($packet->{'prototype'} == $protocol_info_type{'proto_arp'})
			{
				$pinfo .= 'ARP ' . $packet->{'sourceip'} . ' -> ' . $packet->{'destip'};
			}
			elsif($packet->{'prototype'} == $protocol_info_type{'proto_ipx_tcp'})
			{
				$pinfo .= 'IPX';
			}
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_data_cf_ack'})
		{
			$pinfo .= "Data+CF+Ack";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_data_cf_poll'})
		{
			$pinfo .= "Data+CF+Poll";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_data_cf_ack_poll'})
		{
			$pinfo .= "Data+CF+Ack+Poll";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_data_null'})
		{
			$pinfo .= "Data Null";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_cf_ack'})
		{
			$pinfo .= "CF Ack";
		}
		elsif($packet->{'subtype'} == $packet_sub_type{'packet_sub_cf_ack_poll'})
		{
			$pinfo .= "CF Ack+Poll";
		}
		else
		{
			$pinfo .= "Unknown";
		}
	}
	else
	{
		$ptype = 'UNKNOWN';
	}

	return($time, $bssid, $ssid, $srcmac, $dstmac, $ptype, $pinfo);
}

sub xgetservbyport
{
	shift;
	my $port = shift;
	my $proto = shift;
	my $name = getservbyport($port, $proto);
	if($name)
	{
		return $name;
	}
	else
	{
		return $port;
	}
}

1;
